// Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
//
// Licensed under the Mulan PSL v2.
// You can use this file according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of the License at
//
//     http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
// FOR MORE DETAILS.

#include "DBManager/Repository/HDF5/Input/Element/ElementSummaryRepository.h"

#include <unordered_set>

#include "Utility/Logging/SGLogger.h"

#include "DBManager/DBAPI.h"
#include "DBManager/IRepository/Input/Element/ICHEXA20Repository.h"
#include "DBManager/IRepository/Input/Element/ICHEXA8Repository.h"
#include "DBManager/IRepository/Input/Element/ICHEXARepository.h"
#include "DBManager/IRepository/Input/Element/ICPENTA15Repository.h"
#include "DBManager/IRepository/Input/Element/ICPENTA6Repository.h"
#include "DBManager/IRepository/Input/Element/ICPENTARepository.h"
#include "DBManager/IRepository/Input/Element/ICPYRAM13Repository.h"
#include "DBManager/IRepository/Input/Element/ICPYRAM5Repository.h"
#include "DBManager/IRepository/Input/Element/ICPYRAMRepository.h"
#include "DBManager/IRepository/Input/Element/ICTETRA10Repository.h"
#include "DBManager/IRepository/Input/Element/ICTETRA4Repository.h"
#include "DBManager/IRepository/Input/Element/ICTETRARepository.h"
#include "DBManager/IRepository/Input/Element/IElementSummaryRepository.h"
#include "DBManager/Repository/HDF5/Base/HDF5RepositoryMacro.h"

#include "ElementSummaryRepository.h"

HDF5_REG_REPOSITORY (ElementSummaryRepository)

using namespace SG::DBManager::HDF5;

using namespace SG::DBManager;
using namespace SG::DataStructure::Common;
using namespace SG::DataStructure::FEM;

ElementSummaryRepository::ElementSummaryRepository (HDF5Database& database)
    : SingleRepositoryBase<SG::DBManager::SummaryInfo<SG::DataStructure::FEM::ElementTypes>> (database)
{
    for (auto&& [type, method] : *HDF5Hub::GetInitElementReposMethods ())
    {
        method (&GetDatabase ());
    }
};

void ElementSummaryRepository::InitialDatasetInfo ()
{
    RegisterTableInfo<SG::DataStructure::Common::Id> ("/OWN/INDEX/INPUT/ELEMENT/ELEMENTSUMMARY", IndexDataset);
    RegisterTableInfo<SG::DBManager::SummaryInfo<SG::DataStructure::FEM::ElementTypes>> ("/OWN/INPUT/ELEMENT/ELEMENTSUMMARY/Summary",
                                                                                         MainDataset);

    CreateDatasetInFile ();
}

void ElementSummaryRepository::InitDataset ()
{
    SingleRepositoryBase<SG::DBManager::SummaryInfo<SG::DataStructure::FEM::ElementTypes>>::InitDataset ();

    auto insertData = [=] () {
        // 触发持久化信号
        GetDatabase ().GetHDF5Hub ()->m_elementSummarySignal ();
        // 遍历所有index表，进行数据的读取
        for (auto&& i : GetDatabase ().GetHDF5Hub ()->m_elementTypeWithIndexPathMapping)
        {
            // 因为cweld的id为ewid, 与一般的不一样，但是呢，获取类型的时候又要获取到它
            if (i.first == ElementTypes::ELETYPE_Weld)
                continue;
            if (GetDatabase ().GetFile ()->exist (i.second))
            {
                std::vector<SG::DataStructure::Common::Id> idList;
                GetDataset (i.second).read (idList);

                std::vector<SG::DBManager::SummaryInfo<SG::DataStructure::FEM::ElementTypes>> elementSummarys;

                elementSummarys.reserve (idList.size ());

                for (size_t index = 0; index < idList.size (); index++)
                {
                    elementSummarys.emplace_back (idList[index], i.first);
                    // SGInfo << idList[index] << " " << i.first << " " << index;
                }

                Add (elementSummarys);
            }
        }
        // 将实体分开的表合并
        // "/SG/INPUT/ELEMENT/CTETRA10"
        // "/SG/INPUT/ELEMENT/CTETRA4"
        // "/SG/INPUT/ELEMENT/CPYRAM13"
        // "/SG/INPUT/ELEMENT/CPYRAM5"
        // "/SG/INPUT/ELEMENT/CPENTA15"
        // "/SG/INPUT/ELEMENT/CPENTA6"
        // "/SG/INPUT/ELEMENT/CHEXA20"
        // "/SG/INPUT/ELEMENT/CHEXA8"
        std::shared_ptr<ICTETRARepository>   ctetra   = GetDatabase ().GetRepository<ICTETRARepository> ();
        std::shared_ptr<ICTETRA4Repository>  ctetra4  = GetDatabase ().GetRepository<ICTETRA4Repository> ();
        std::shared_ptr<ICTETRA10Repository> ctetra10 = GetDatabase ().GetRepository<ICTETRA10Repository> ();

        auto ctetradata1 = ctetra4->GetAllData ();
        auto ctetradata2 = ctetra10->GetAllData ();
        for (auto data : ctetradata1)
        {
            auto d = static_cast<SG::DataStructure::FEM::CTETRAData> (data);
            ctetra->Add (d);
        }
        for (auto data : ctetradata2)
        {
            auto d = static_cast<SG::DataStructure::FEM::CTETRAData> (data);
            ctetra->Add (d);
        }

        std::shared_ptr<ICPENTARepository>   cpenta   = GetDatabase ().GetRepository<ICPENTARepository> ();
        std::shared_ptr<ICPENTA6Repository>  cpenta6  = GetDatabase ().GetRepository<ICPENTA6Repository> ();
        std::shared_ptr<ICPENTA15Repository> cpenta15 = GetDatabase ().GetRepository<ICPENTA15Repository> ();

        auto cpentadata1 = cpenta6->GetAllData ();
        auto cpentadata2 = cpenta15->GetAllData ();
        for (auto data : cpentadata1)
        {
            auto d = static_cast<SG::DataStructure::FEM::CPENTAData> (data);
            cpenta->Add (d);
        }
        for (auto data : cpentadata2)
        {
            auto d = static_cast<SG::DataStructure::FEM::CPENTAData> (data);
            cpenta->Add (d);
        }

        std::shared_ptr<ICPYRAMRepository>   cpyram   = GetDatabase ().GetRepository<ICPYRAMRepository> ();
        std::shared_ptr<ICPYRAM5Repository>  cpyram5  = GetDatabase ().GetRepository<ICPYRAM5Repository> ();
        std::shared_ptr<ICPYRAM13Repository> cpyram13 = GetDatabase ().GetRepository<ICPYRAM13Repository> ();

        auto cpyramdata1 = cpyram5->GetAllData ();
        auto cpyramdata2 = cpyram13->GetAllData ();
        for (auto data : cpyramdata1)
        {
            auto d = static_cast<SG::DataStructure::FEM::CPYRAMData> (data);
            cpyram->Add (d);
        }
        for (auto data : cpyramdata2)
        {
            auto d = static_cast<SG::DataStructure::FEM::CPYRAMData> (data);
            cpyram->Add (d);
        }

        std::shared_ptr<ICHEXARepository>   chexa   = GetDatabase ().GetRepository<ICHEXARepository> ();
        std::shared_ptr<ICHEXA8Repository>  chexa8  = GetDatabase ().GetRepository<ICHEXA8Repository> ();
        std::shared_ptr<ICHEXA20Repository> chexa20 = GetDatabase ().GetRepository<ICHEXA20Repository> ();

        auto chexadata1 = chexa8->GetAllData ();
        auto chexadata2 = chexa20->GetAllData ();
        for (auto data : chexadata1)
        {
            auto d = static_cast<SG::DataStructure::FEM::CHEXAData> (data);
            chexa->Add (d);
        }
        for (auto data : chexadata2)
        {
            auto d = static_cast<SG::DataStructure::FEM::CHEXAData> (data);
            chexa->Add (d);
        }
    };

    // 如果数据集为空，则插入数据
    // 这里的判断是为了避免重复插入数据，通常在第一次初始化时
    // 数据集可能为空，后续操作会自动填充数据。
    // 但如果数据集已经存在数据，则不需要再次插入。
    // 这可以避免重复插入相同的数据，导致数据冗余或错误。
    if (GetMainDatasetInfo ().GetRowCount () == 0)
    {
        insertData ();
        SortData ();
    }
}

std::vector<SG::DataStructure::FEM::ElementTypes> ElementSummaryRepository::GetAllElementTypes ()
{
    // 获取所有键值
    std::vector<SG::DBManager::SummaryInfo<SG::DataStructure::FEM::ElementTypes>> sums;
    sums.reserve (GetMainDatasetInfo ().GetRowCount ());
    GetDataset (MainDataset).read (sums);

    // 使用 set 存储不同的 m_type 值
    std::vector<SG::DataStructure::FEM::ElementTypes> types;

    // 构建反向映射
    // std::map<std::string, SG::DataStructure::FEM::ElementTypes> pathTypeMap;
    // for (auto&& i : GetDatabase ().GetHDF5Hub ()->m_elementTypeWithIndexPathMapping)
    // {
    //     pathTypeMap[i.second] = i.first;
    // }

    // 便利所有index表，进行数据的读取
    for (auto&& i : GetDatabase ().GetHDF5Hub ()->m_elementTypeWithIndexPathMapping)
    {
        if (GetDatabase ().GetFile ()->exist (i.second) && GetDataset (i.second).getElementCount () > 0)
        {
#if defined(SG_USE_MPI) && defined(H5_HAVE_PARALLEL)
            std::vector<SG::DataStructure::Common::Id> idList;
            GetDataset (i.second).read (idList);

            MainlyRepositoryBase<DataType>::ElementIdFilter (GetDatabase (), idList);

            if (idList.size () > 0)
                types.emplace_back (i.first);
#else
            types.emplace_back (i.first);
#endif
        }
    }

    return types;
};

SG::DataStructure::FEM::ElementTypes ElementSummaryRepository::GetElementType (SG::DataStructure::Common::Id Eid)
{
    SG::DBManager::SummaryInfo<SG::DataStructure::FEM::ElementTypes> data;  // NOLINT(misc-const-correctness)
    if (FindById (Eid, data) == SG::DataStructure::Common::Status::STATUS_Success)
    {
        return data.m_type;
    }

    return SG::DataStructure::FEM::ElementTypes::ELETYPE_Unknown;
};

/// @brief 类型为`type`的单元的数量
std::size_t ElementSummaryRepository::GetElementSize (SG::DataStructure::FEM::ElementTypes type)
{
    // 便利所有index表，进行数据的读取
    for (auto&& i : GetDatabase ().GetHDF5Hub ()->m_elementTypeWithIndexPathMapping)
    {
        if (i.first == type && GetDatabase ().GetFile ()->exist (i.second))
        {
#if defined(SG_USE_MPI) && defined(H5_HAVE_PARALLEL)
            std::vector<SG::DataStructure::Common::Id> idList;
            GetDataset (i.second).read (idList);

            ElementIdFilter (GetDatabase (), idList);

            return idList.size ();
#else
            return GetDataset (i.second).getElementCount ();
#endif
        }
    }

    return 0;
};

/// @brief 获取指定类型的所有单元ID
std::vector<SG::DataStructure::Common::Id> ElementSummaryRepository::GetElementIdsByType (SG::DataStructure::FEM::ElementTypes type)
{
    std::vector<SG::DataStructure::Common::Id> idList;

    // 遍历所有index表，查找指定类型的单元
    for (auto&& i : GetDatabase ().GetHDF5Hub ()->m_elementTypeWithIndexPathMapping)
    {
        if (i.first == type && GetDatabase ().GetFile ()->exist (i.second))
        {
            GetDataset (i.second).read (idList);
#ifdef SG_USE_MPI
            ElementIdFilter (GetDatabase (), idList);
#endif
            break;
        }
    }

    return idList;
};

std::map<SG::DataStructure::Common::I64, SG::DBManager::TwoLayersData> ElementSummaryRepository::GetP2EMap ()
{
    std::map<I64, TwoLayersData> results;

    // 便利所有index表，进行数据的读取
    for (auto&& fun : *GetDatabase ().GetHDF5Hub ()->GetP2EFunList ())
    {
        std::map<I64, std::vector<Id>> tempResult = fun (&GetDatabase ());
        for (auto&& i : tempResult)
        {
            if (results.find (i.first) == results.end ())
            {
                results[i.first] = TwoLayersData (i.first, i.second);
            }
            else
            {
                results[i.first].m_subs.insert (results[i.first].m_subs.end (), i.second.begin (), i.second.end ());
            }
        }
    }

    return results;
}

SG::Algebra::Matrix ElementSummaryRepository::GetElementNodes (SG::DataStructure::FEM::ElementTypes type)
{
    SG::Algebra::Matrix result;

    // 便利所有index表，进行数据的读取
    for (auto&& [etype, fun] : *GetDatabase ().GetHDF5Hub ()->GetElementNodesFunMap ())
    {
        if (etype != type)
            continue;
        result = fun (&GetDatabase ());
        if (result.GetRowSize () > 0 && result.GetColSize () > 0)
        {
            return result;
        }
    }

    SGError << "No element nodes found for the specified type.";
    return SG::Algebra::Matrix ();
}

void ElementSummaryRepository::IdFilter (std::vector<SG::DataStructure::Common::Id>& ids)
{
#if defined(SG_USE_MPI)
    ElementIdFilter (GetDatabase (), ids);  // 调用 ElementIdFilter 函数进行 ID 过滤
#endif
}